<?php declare(strict_types=1);

namespace think\pos\provider\lipos;

use Exception;
use shali\phpmate\core\util\RandomUtil;
use shali\phpmate\core\util\StrUtil;
use shali\phpmate\crypto\EncryptUtil;
use shali\phpmate\crypto\KeyUtil;
use shali\phpmate\crypto\SignUtil;
use shali\phpmate\http\HttpClient;
use shali\phpmate\PhpMateException;
use shali\phpmate\util\Money;
use think\pos\constant\AccountType;
use think\pos\dto\request\AccountBalanceRequestDto;
use think\pos\dto\request\CallbackRequest;
use think\pos\dto\request\MerchantRequestDto;
use think\pos\dto\request\PosRequestDto;
use think\pos\dto\request\SimRequestDto;
use think\pos\dto\request\WithdrawQueryRequestDto;
use think\pos\dto\request\WithdrawRequestDto;
use think\pos\dto\response\AccountBalanceResponse;
use think\pos\dto\response\PosInfoResponse;
use think\pos\dto\response\PosProviderResponse;
use think\pos\dto\response\WithdrawResponse;
use think\pos\exception\ProviderGatewayException;
use think\pos\PosStrategy;
use think\pos\provider\lipos\convertor\MerchantConvertor;
use think\pos\provider\lipos\convertor\PosConvertor;
use think\pos\provider\lipos\convertor\WithdrawConvertor;

/**
 * 力 POS 对接
 */
class LiPosStrategy extends PosStrategy
{
    /**
     * 回调成功返回内容
     */
    protected const CALLBACK_ACK_CONTENT = 'OK';

    /**
     * 响应 00 表示成功
     */
    private const RESPONSE_CODE_SUCCESS = '00';

    /**
     * 响应失败 code
     */
    private const RESPONSE_CODE_FAIL_MAP = [
        // 解绑接口返回的错误码
        'pos_not_exist' => '1101',// 代理商不存在此 pos
        'pos_not_bind' => '1102',// 商户未绑定此 pos
    ];

    /**
     * 接口方法
     */
    private const API_METHOD = [
        // 商户绑定 pos
        'bind_pos' => '/bindMaterials',
        // 商户解绑 pos
        'unbind_pos' => '/unBindMaterials',
        // 查询 pos 终端
        'pos_info' => '/materialsDataQuery',
        // 设置 pos 费率
        'modify_pos_rate' => '/batchSetDefaultRate',
        // 设置 pos 通信服务费
        'modify_pos_sim_fee' => '/simFeeChange',
        // 设置 pos 押金=服务费
        'modify_pos_deposit' => '/machineFeeChange',
        // 设置商户费率
        'modify_merchant_rate' => '/customerRateModify',
        // 账户余额查询
        'account_balance_query' => '/accountBalanceQuery',
        // 代付申请
        'submit_withdraw' => '/submitWithdraw/v2',
        // 代付查询
        'withdraw_result_query' => '/withdrawResultQuery/v2',
    ];

    /**
     * 力 pos 平台回调服务类型
     */
    private const CALLBACK_SERVICE_TYPE = [
        // 商户费率设置成功回调
        'merchant_rate_set_success' => 'CUSTOMER_LAST_RATE_NOTIFY',
        // 商户注册成功回调商户信息
        'merchant_register_success' => 'CUSTOMER_INFO_REGISTER_NOTIFY',
        // pos 绑定成功回调
        'pos_bind_success' => 'MATERIAL_BIND_STATUS_NOTIFY',
        // pos 激活成功回调
        'pos_activate_success' => 'MATERIALS_ACTIVATION_NOTIFY',
        // 刷卡交易成功通知回调
        'pos_trans_success' => 'PAY_ORDER_NOTIFY',
    ];

    /**
     * @var HttpClient
     */
    private $httpClient;

    public static function providerName(): string
    {
        return '力POS';
    }

    public function __construct(array $config)
    {
        parent::__construct($config);
        $this->httpClient = new HttpClient();
    }


    /**
     * 查询账户余额
     * @param AccountBalanceRequestDto $dto
     * @return AccountBalanceResponse
     */
    function getAccountBalance(AccountBalanceRequestDto $dto): AccountBalanceResponse
    {
        $accountType = $dto->getAccountType();
        if (!$this->checkAccountType($accountType)) {
            return AccountBalanceResponse::fail('账户类型错误');
        }
        $url = $this->getUrl(self::API_METHOD['account_balance_query'],true);
        $params = [];
        $params['accountType'] = $accountType;

        try {
            $res = $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]查询账户余额失败：%s', self::providerName(), $e->getMessage());
            return AccountBalanceResponse::fail($errorMsg);
        }

        return WithdrawConvertor::toAccountBalanceResponse($res);
    }

    /**
     * 代付申请
     * @param WithdrawRequestDto $dto
     * @return WithdrawResponse
     */
    function submitWithdraw(WithdrawRequestDto $dto): WithdrawResponse
    {
        $url = $this->getUrl(self::API_METHOD['submit_withdraw'],true);
        $accountType = $dto->getAccountType();
        if (!$this->checkAccountType($accountType)) {
            return WithdrawResponse::fail('账户类型错误');
        }
        $params = [
            'orderNo' => $dto->getOrderNo(),
            'amount' => $dto->getAmount()->toYuan(),
            'accountType' => $accountType,
            'settleType' => $dto->getSettleType(),
            'accountNo' => $dto->getAccountNo(),
            'accountName' => $dto->getAccountName(),
            'bankName' => $dto->getBankName(),
        ];

        // 可选参数
        if (!empty($dto->getBranchName())) {
            $params['branchName'] = $dto->getBranchName();
        }
        if (!empty($dto->getUnionCode())) {
            $params['unionCode'] = $dto->getUnionCode();
        }
        if (!empty($dto->getPhoneNo())) {
            $params['phoneNo'] = $dto->getPhoneNo();
        }
        if (!empty($dto->getIdCard())) {
            $params['idCard'] = $dto->getIdCard();
        }
        if ($dto->getEntrustAmount() !== null) {
            $params['entrustAmount'] = $dto->getEntrustAmount()->toYuan();
        }

        try {
            $res = $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]代付申请失败：%s', self::providerName(), $e->getMessage());
            return WithdrawResponse::fail($errorMsg);
        }

        return WithdrawConvertor::toWithdrawResponse($res);
    }

    /**
     * 代付查询
     * @param WithdrawQueryRequestDto $dto
     * @return WithdrawResponse
     */
    function queryWithdraw(WithdrawQueryRequestDto $dto): WithdrawResponse
    {
        $url = $this->getUrl(self::API_METHOD['withdraw_result_query'],true);
        $params = [
            'orderNo' => $dto->getOrderNo(),
        ];

        try {
            $res = $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]代付查询失败：%s', self::providerName(), $e->getMessage());
            return WithdrawResponse::fail($errorMsg);
        }

        return WithdrawConvertor::toWithdrawQueryResponse($res);
    }

    /**
     * pos 初始化
     * @param PosRequestDto $dto
     * @return PosProviderResponse
     */
    public function initPosConfig(PosRequestDto $dto): PosProviderResponse
    {
        // 初始化代理费率
        $posProviderResponse = $this->setPosRate($dto);
        if ($posProviderResponse->isFail()) {
            return $posProviderResponse;
        }
        // 初始化代理押金
        return $this->setPosDeposit($dto);
    }

    function getPosInfo(PosRequestDto $dto): PosInfoResponse
    {
        $url = $this->getUrl(self::API_METHOD['pos_info']);
        $params = [
            'materialsNo' => $dto->getDeviceSn(),
        ];
        try {
            $res = $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]查询pos_sn=%s信息失败：%s', self::providerName(), $dto->getDeviceSn(), $e->getMessage());
            return PosInfoResponse::fail($errorMsg);
        }

        return PosConvertor::toPosInfoResponse($res);
    }

    /**
     * 修改 pos 费率
     * @param PosRequestDto $dto
     * @return PosProviderResponse
     */
    function setPosRate(PosRequestDto $dto): PosProviderResponse
    {
        if (false === $this->cappingValueCheck($dto->getDebitCardCappingValue())) {
            return PosProviderResponse::fail('借记卡封顶金额区间为 [18, 25]');
        }
        $url = $this->getUrl(self::API_METHOD['modify_pos_rate']);
        $params = [
            // 该接口支持批量更新，我们还不支持
            'materialsNoList' => [$dto->getDeviceSn(),],
            'materialsDefaultRateList' => [
                // 信用卡，4 种特惠使用贷记卡的费率
                [
                    // 信用卡
                    'payTypeViewCode' => 'POS_CC',
                    // 费率
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    // 附加费用
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    // 贷记卡封顶值
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_CC',
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_GF_CC',
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_PA_CC',
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_MS_CC',
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                // 借记卡
                [
                    'payTypeViewCode' => 'POS_DC',
                    'rateValue' => $dto->getDebitCardRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => $dto->getDebitCardCappingValue()->toYuan(),
                ],
                // 微信，云闪付1000- 统一使用微信费率
                [
                    'payTypeViewCode' => 'WECHAT',
                    'rateValue' => $dto->getWechatRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'UNIONPAY_DOWN_CC',
                    'rateValue' => $dto->getWechatRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => '0.00',
                ],
                // 支付宝
                [
                    'payTypeViewCode' => 'ALIPAY',
                    'rateValue' => $dto->getAlipayRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => '0.00',
                ],
            ]
        ];
        try {
            $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]修改pos_sn=%s费率失败：%s', self::providerName(), $dto->getDeviceSn(), $e->getMessage());
            return PosProviderResponse::fail($errorMsg);
        }
        return PosProviderResponse::success();
    }

    /**
     * 设置 pos 通信服务费，其实就是 pos 机里的 esim 流量费
     * 流量卡套餐 getSimPackageCode 格式：[{"simRuleIndex": "通讯服务费阶段 integer", "simPhaseIndex": "通讯服务费档位 integer", "beginDayNum": "通讯服务费扣费起始天数 integer"}]
     * @param SimRequestDto $dto
     * @return PosProviderResponse
     */
    function setSimFee(SimRequestDto $dto): PosProviderResponse
    {
        $url = $this->getUrl(self::API_METHOD['modify_pos_sim_fee']);
        $params = [
            'materialsNo' => $dto->getDeviceSn(),
            // 套餐信息，自行去力 pos 平台配置，然后按照 pos 查询返回
            'simDeductionsList' => json_decode($dto->getSimPackageCode(), true),
        ];
        try {
            $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]修改pos_sn=%s通信费失败：%s', self::providerName(), $dto->getDeviceSn(), $e->getMessage());
            return PosProviderResponse::fail($errorMsg);
        }
        return PosProviderResponse::success();
    }

    /**
     * 设置终端押金 = 服务费设置
     * 注意：绑定了商户的 pos 不能设置押金
     * @param PosRequestDto $dto
     * @return PosProviderResponse
     */
    public function setPosDeposit(PosRequestDto $dto): PosProviderResponse
    {
        $url = $this->getUrl(self::API_METHOD['modify_pos_deposit']);
        $params = [
            'materialsNo' => $dto->getDeviceSn(),
            // 押金档位 integer
            'machinePhaseIndex' => intval($dto->getDepositPackageCode()),
        ];
        try {
            $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]修改pos_sn=%s押金失败：%s', self::providerName(), $dto->getDeviceSn(), $e->getMessage());
            return PosProviderResponse::fail($errorMsg);
        }
        return PosProviderResponse::success();
    }

    /**
     * 商户绑定 pos
     */
    public function bindPos(MerchantRequestDto $merchantRequestDto, PosRequestDto $posRequestDto): PosProviderResponse
    {
        $url = $this->getUrl(self::API_METHOD['bind_pos']);
        $params = [
            'customerNo' => $merchantRequestDto->getMerchantNo(),
            'materialsNo' => $posRequestDto->getDeviceSn(),
        ];
        try {
            $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]绑定pos_sn=%s失败：%s', self::providerName(), $posRequestDto->getDeviceSn(), $e->getMessage());
            return PosProviderResponse::fail($errorMsg);
        }
        return PosProviderResponse::success();
    }

    /**
     * 商户解绑 pos
     * @param MerchantRequestDto $merchantRequestDto
     * @param PosRequestDto $posRequestDto
     * @return PosProviderResponse
     */
    public function unbindPos(MerchantRequestDto $merchantRequestDto, PosRequestDto $posRequestDto): PosProviderResponse
    {
        $url = $this->getUrl(self::API_METHOD['unbind_pos']);
        $params = [
            'customerNo' => $merchantRequestDto->getMerchantNo(),
            'materialsNo' => $posRequestDto->getDeviceSn(),
        ];
        try {
            $this->post($url, $params);
        } catch (Exception $e) {
            $errMsg = $e->getMessage();
            $errCode = substr($errMsg, 5, strpos($errMsg, '&') - 5);
            if (in_array($errCode, [
                self::RESPONSE_CODE_FAIL_MAP['pos_not_exist'],
                self::RESPONSE_CODE_FAIL_MAP['pos_not_bind'],
            ])) {
                // 应对用户在力pos平台解绑后，再调用解绑接口
                return PosProviderResponse::success();
            }
            $errorMsg = sprintf('pos服务商[%s]解绑pos_sn=%s失败：%s', self::providerName(), $posRequestDto->getDeviceSn(), $e->getMessage());
            return PosProviderResponse::fail($errorMsg);
        }

        return PosProviderResponse::success();
    }

    /**
     * 修改商户费率
     * @param MerchantRequestDto $dto
     * @return PosProviderResponse
     */
    function setMerchantRate(MerchantRequestDto $dto): PosProviderResponse
    {
        if (false === $this->cappingValueCheck($dto->getDebitCardCappingValue())) {
            return PosProviderResponse::fail('借记卡封顶金额区间为 [18, 25]');
        }
        $url = $this->getUrl(self::API_METHOD['modify_merchant_rate']);
        $params = [
            'customerNo' => $dto->getMerchantNo(),
            'customerRateList' => [
                // 信用卡，4 种特惠使用贷记卡的费率
                [
                    // 费率类型
                    'payTypeViewCode' => 'POS_CC',
                    // 费率
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    // 附加费用
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    // 贷记卡封顶值
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_CC',// 特惠
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_GF_CC',// 特惠 GF
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_PA_CC',// 特惠 PA
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'POS_DISCOUNT_MS_CC',// 特惠 MS
                    'rateValue' => $dto->getCreditRate()->toPercentage(6),
                    'fixedValue' => $dto->getWithdrawFee()->toYuan(),
                    'cappingValue' => '0.00',
                ],
                // 借记卡
                [
                    'payTypeViewCode' => 'POS_DC',
                    'rateValue' => $dto->getDebitCardRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => $dto->getDebitCardCappingValue()->toYuan(),
                ],
                // 微信，云闪付1000- 统一使用微信费率
                [
                    'payTypeViewCode' => 'WECHAT',
                    'rateValue' => $dto->getWechatRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => '0.00',
                ],
                [
                    'payTypeViewCode' => 'UNIONPAY_DOWN_CC',
                    'rateValue' => $dto->getWechatRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => '0.00',
                ],
                // 支付宝
                [
                    'payTypeViewCode' => 'ALIPAY',
                    'rateValue' => $dto->getAlipayRate()->toPercentage(6),
                    'fixedValue' => '0.00',
                    'cappingValue' => '0.00',
                ],
            ],
        ];
        try {
            $this->post($url, $params);
        } catch (Exception $e) {
            $errorMsg = sprintf('pos服务商[%s]修改商户merchant_no=%s费率失败：%s', self::providerName(), $dto->getMerchantNo(), $e->getMessage());
            return PosProviderResponse::fail($errorMsg);
        }

        return PosProviderResponse::success();
    }

    private function getUrl(string $apiMethod,bool $isWithdraw=false): string
    {
        if ($isWithdraw) {
            $gateway = $this->isTestMode() ? $this->config['testWithdrawGateway'] : $this->config['withdrawGateway'];
        } else {
            $gateway = $this->isTestMode() ? $this->config['testGateway'] : $this->config['gateway'];
        }

        return $gateway . $apiMethod;
    }

    /**
     * aes 对称加密，填充模式 PKCS5Padding
     * 1. 随机一个 16 位的密钥，并使用该密钥进行 aes-ecb-pkcs5padding 对称加密请求 json 数据
     * 2. 利用平台的公钥对随机密钥进行非对称加密，并提交给平台
     * 签名：
     * 使用 SHA256WithRSA 进行签名
     * @throws Exception
     */
    private function post(string $url, array $data): array
    {
        // 加密和签名
        $password = RandomUtil::randomString(16);
        $params['data'] = $this->encryptData($password, json_encode($data));
        $params['encryptKey'] = $this->encryptPassword($password);
        $params['appId'] = $this->config['agentNo'];
        $params['timestamp'] = time();
        $params['sign'] = $this->sign($params);
        try {
            $res = $this->httpClient->post($url, $params, ['Content-Type' => 'application/json']);
        } finally {
            $this->rawRequest = $this->httpClient->getRawRequest();
            // 请求参数记录明文
            $this->rawRequest['params']['data'] = $data;
            $this->rawResponse = $this->httpClient->getRawResponse();
        }
        $res = json_decode($res, true);
        // 验签->检查是否成功->解密
        try {
            if (!empty($res['sign']) && false === $this->verifySign($res)) {
                throw new ProviderGatewayException('验签失败');
            }
            // 检查
            if ($res['code'] !== self::RESPONSE_CODE_SUCCESS) {
                $errorMsg = sprintf('code=%s&msg=%s', $res['code'], $res['msg']);
                throw new ProviderGatewayException($errorMsg);
            }
            $content = $this->decrypt($res['encryptKey'], $res['data']);
            // 日志记录明文
            $this->rawRequest['params'] = $data;
            $this->rawResponse['decryptedBody'] = $content;
            // 25年5月21日，鉴于力POS很多设置接口，返回消息内容格式不统一，这里做兼容
            return StrUtil::isJson($content) ? json_decode($content, true) : [$content];
        } catch (PhpMateException $e) {
            $errorMsg = sprintf('请求响应数据验签->解密异常：%s', $e->getMessage());
            throw new ProviderGatewayException($errorMsg);
        }
    }

    /**
     * @throws PhpMateException
     */
    function sign(array $data): string
    {
        // 字典序
        unset($data['sign'], $data['success']);
        $content = StrUtil::httpBuildQuery($data, true);
        return SignUtil::signBySHA256withRSAToBase64(KeyUtil::toPrivateKeyValueOfBase64Str($this->config['privateKey']), $content);
    }

    /**
     * @throws PhpMateException
     */
    public function verifySign(array $data): bool
    {
        $sign = $data['sign'];
        unset($data['success'], $data['sign']);
        $content = StrUtil::httpBuildQuery($data, true);
        return SignUtil::verifySignBySHA256withRSAToBase64(KeyUtil::toPublicKeyValueOfBase64Str($this->config['platformPublicKey']), $sign, $content);
    }

    /**
     * @throws PhpMateException
     */
    public function decrypt(string $encryptKey, string $encrypted): string
    {
        $password = EncryptUtil::decryptByRSA_ECB_PKCS1PaddingToBase64(KeyUtil::toPrivateKeyValueOfBase64Str($this->config['privateKey']), $encryptKey);
        return EncryptUtil::decryptByAES_ECB_PKCS5PaddingToBase64($password, $encrypted);
    }

    /**
     * 使用对称加密密码加密数据
     * @throws PhpMateException
     */
    public function encryptData(string $password, string $json): string
    {
        return EncryptUtil::encryptByAES_ECB_PKCS5PaddingToBase64($password, $json);
    }

    /**
     * 加密对称加密数据的密码
     * @throws PhpMateException
     */
    public function encryptPassword(string $password): string
    {
        return EncryptUtil::encryptByRSA_ECB_PKCS1PaddingToBase64(KeyUtil::toPublicKeyValueOfBase64Str($this->config['platformPublicKey']), $password);
    }

    /**
     * @throws ProviderGatewayException
     */
    public function handleCallback(string $content)
    {
        $data = json_decode($content, true);
        $this->rawRequest = $data;
        if (empty($data['serviceType']) || empty($data['data']) || empty($data['encryptKey'])) {
            // 非力 pos 回调，拉倒吧不处理
            return CallbackRequest::fail('非力 pos 回调，无法处理');
        }
        try {
            // 验签
            if (!empty($data['sign']) && false === $this->verifySign($data)) {
                $errorMsg = sprintf('力 pos %s 回调验签失败', $data['serviceType']);
                return CallbackRequest::fail($errorMsg);
            }
            // 解密
            $decrypted = $this->decrypt($data['encryptKey'], $data['data']);
        } catch (PhpMateException $e) {
            $errorMsg = sprintf('回调请求数据验签->解密异常：%s', $e->getMessage());
            throw new ProviderGatewayException($errorMsg);
        }
        $decryptedData = json_decode($decrypted, true);
        $this->rawRequest['data'] = $decryptedData;
        $this->rawResponse = $this->getCallbackAckContent();
        if (self::CALLBACK_SERVICE_TYPE['merchant_rate_set_success'] === $data['serviceType']) {
            return MerchantConvertor::toMerchantRateSetCallbackRequest($decryptedData);
        } elseif (self::CALLBACK_SERVICE_TYPE['merchant_register_success'] === $data['serviceType']) {
            return MerchantConvertor::toMerchantRegisterCallbackRequest($decryptedData);
        } elseif (self::CALLBACK_SERVICE_TYPE['pos_activate_success'] === $data['serviceType']) {
            return PosConvertor::toPosActivateCallbackRequest($decryptedData);
        } elseif (self::CALLBACK_SERVICE_TYPE['pos_bind_success'] === $data['serviceType']) {
            return PosConvertor::toPosBindCallbackRequest($decryptedData);
        } elseif (self::CALLBACK_SERVICE_TYPE['pos_trans_success'] === $data['serviceType']) {
            return PosConvertor::toPosTransCallbackRequest($decryptedData);
        }

        $errorMsg = '';
        $this->rawResponse = $errorMsg;
        return CallbackRequest::fail($errorMsg);
    }

    /**
     * 力 pos 限制借记卡封顶金额 [18, 25]
     * @param Money $debitCardCappingValue
     * @return bool
     */
    private function cappingValueCheck(Money $debitCardCappingValue): bool
    {
        $yuan = $debitCardCappingValue->toYuan();
        if (bccomp($yuan, '18', 2) < 0) {
            return false;
        } elseif (bccomp('25', $yuan, 2) < 0) {
            return false;
        }

        return true;
    }

    /**
     * 力 POS 校验账户类型
     * */
    private function checkAccountType(string $accountType): bool
    {
        return in_array($accountType,[AccountType::SHARE,AccountType::ACTIVITY_CASHBACK,AccountType::ACTIVITY_CASHBACK,AccountType::ACTIVITY_SUBSIDY,AccountType::SIM_CARD,AccountType::COMMISSION]);
    }
}